﻿using System;
using MathExpression;
using NUnit.Framework;

namespace LPTask.Test
{
    [TestFixture]
    public class MathExpressionOperationsFixture
    {
        [Test]
        public void ScalarAddition()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0);
            IMathExpression operand2 = ExpressionFactory.Create(4.0);

            IMathExpression result = operand1.Add(operand2);

            var data = result.Data as ScalarData;
            Assert.IsNotNull(data);
            Assert.AreEqual(7.0, data.Value);
        }

        [Test]
        public void ScalarPolynomAddition()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0);
            IMathExpression operand2 = ExpressionFactory.Create(4.0, 5.0);

            IMathExpression result = operand1.Add(operand2);

            var resultData = result.Data as PolynomialData;
            Assert.IsNotNull(resultData);
            Assert.IsNotEmpty(resultData.Values);
            Assert.AreEqual(2, resultData.Values.Count);

            var freeValue = resultData.Values[0].Data as ScalarData;
            Assert.IsNotNull(freeValue);
            Assert.AreEqual(7.0, freeValue.Value);

            var parameterValue = resultData.Values[1].Data as ScalarData;
            Assert.IsNotNull(parameterValue);
            Assert.AreEqual(5.0, parameterValue.Value);
        }

        [Test]
        public void PolynomScalarAddition()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0);
            IMathExpression operand2 = ExpressionFactory.Create(4.0, 5.0);

            IMathExpression result = operand2.Add(operand1);

            var resultData = result.Data as PolynomialData;
            Assert.IsNotNull(resultData);
            Assert.IsNotEmpty(resultData.Values);
            Assert.AreEqual(2, resultData.Values.Count);

            var freeValue = resultData.Values[0].Data as ScalarData;
            Assert.IsNotNull(freeValue);
            Assert.AreEqual(7.0, freeValue.Value);

            var parameterValue = resultData.Values[1].Data as ScalarData;
            Assert.IsNotNull(parameterValue);
            Assert.AreEqual(5.0, parameterValue.Value);
        }

        [Test]
        public void PolynomAddition()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0, 4.0);
            IMathExpression operand2 = ExpressionFactory.Create(5.0, 6.0);

            IMathExpression result = operand1.Add(operand2);

            var resultData = result.Data as PolynomialData;
            Assert.IsNotNull(resultData);
            Assert.IsNotEmpty(resultData.Values);
            Assert.AreEqual(2, resultData.Values.Count);

            var freeValue = resultData.Values[0].Data as ScalarData;
            Assert.IsNotNull(freeValue);
            Assert.AreEqual(8.0, freeValue.Value);

            var parameterValue = resultData.Values[1].Data as ScalarData;
            Assert.IsNotNull(parameterValue);
            Assert.AreEqual(10.0, parameterValue.Value);
        }

        [Test]
        public void ScalarSubtraction()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0);
            IMathExpression operand2 = ExpressionFactory.Create(4.0);

            IMathExpression result = operand1.Subtract(operand2);

            var data = result.Data as ScalarData;
            Assert.IsNotNull(data);
            Assert.AreEqual(-1.0, data.Value);
        }

        [Test]
        public void ScalarPolynomSubtraction()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0);
            IMathExpression operand2 = ExpressionFactory.Create(4.0, 5.0);

            IMathExpression result = operand1.Subtract(operand2);

            var resultData = result.Data as PolynomialData;
            Assert.IsNotNull(resultData);
            Assert.IsNotEmpty(resultData.Values);
            Assert.AreEqual(2, resultData.Values.Count);

            var freeValue = resultData.Values[0].Data as ScalarData; 
            Assert.IsNotNull(freeValue);
            Assert.AreEqual(-1.0, freeValue.Value);

            var parameterValue = resultData.Values[1].Data as ScalarData;
            Assert.IsNotNull(parameterValue);
            Assert.AreEqual(-5.0, parameterValue.Value);
        }

        [Test]
        public void PolynomScalarSubtraction()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0);
            IMathExpression operand2 = ExpressionFactory.Create(4.0, 5.0);

            IMathExpression result = operand2.Subtract(operand1);

            var resultData = result.Data as PolynomialData;
            Assert.IsNotNull(resultData);
            Assert.IsNotEmpty(resultData.Values);
            Assert.AreEqual(2, resultData.Values.Count);

            var freeValue = resultData.Values[0].Data as ScalarData;
            Assert.IsNotNull(freeValue);
            Assert.AreEqual(1.0, freeValue.Value);

            var parameterValue = resultData.Values[1].Data as ScalarData;
            Assert.IsNotNull(parameterValue);
            Assert.AreEqual(5.0, parameterValue.Value);
        }

        [Test]
        public void PolynomSubtraction()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0, 4.0);
            IMathExpression operand2 = ExpressionFactory.Create(5.0, 7.0);

            IMathExpression result = operand1.Subtract(operand2);

            var resultData = result.Data as PolynomialData;
            Assert.IsNotNull(resultData);
            Assert.IsNotEmpty(resultData.Values);
            Assert.AreEqual(2, resultData.Values.Count);

            var freeValue = resultData.Values[0].Data as ScalarData;
            Assert.IsNotNull(freeValue);
            Assert.AreEqual(-2.0, freeValue.Value);

            var parameterValue = resultData.Values[1].Data as ScalarData;
            Assert.IsNotNull(parameterValue);
            Assert.AreEqual(-3.0, parameterValue.Value);
        }

        [Test]
        public void ScalarMultiply()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0);
            IMathExpression operand2 = ExpressionFactory.Create(4.0);

            IMathExpression result = operand1.Multiply(operand2);

            var data = result.Data as ScalarData;
            Assert.IsNotNull(data);
            Assert.AreEqual(12.0, data.Value);
        }

        [Test]
        public void ScalarPolynomMultiply()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0);
            IMathExpression operand2 = ExpressionFactory.Create(4.0, 5.0);

            IMathExpression result = operand1.Multiply(operand2);

            var resultData = result.Data as PolynomialData;
            Assert.IsNotNull(resultData);
            Assert.IsNotEmpty(resultData.Values);
            Assert.AreEqual(2, resultData.Values.Count);

            var freeValue = resultData.Values[0].Data as ScalarData;
            Assert.IsNotNull(freeValue);            
            Assert.AreEqual(12.0, freeValue.Value);

            var parameterValue = resultData.Values[1].Data as ScalarData;
            Assert.IsNotNull(parameterValue);
            Assert.AreEqual(15.0, parameterValue.Value);
        }

        [Test]
        public void PolynomScalarMutiply()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0);
            IMathExpression operand2 = ExpressionFactory.Create(4.0, 5.0);

            IMathExpression result = operand2.Multiply(operand1);

            var resultData = result.Data as PolynomialData;
            Assert.IsNotNull(resultData);
            Assert.IsNotEmpty(resultData.Values);
            Assert.AreEqual(2, resultData.Values.Count);

            var freeValue = resultData.Values[0].Data as ScalarData;
            Assert.IsNotNull(freeValue);
            Assert.AreEqual(12.0, freeValue.Value);

            var parameterValue = resultData.Values[1].Data as ScalarData;
            Assert.IsNotNull(parameterValue);
            Assert.AreEqual(15.0, parameterValue.Value);
        }

        [Test]
        public void PolynomMultiplySimple()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0, 4.0);
            IMathExpression operand2 = ExpressionFactory.Create(5.0, 7.0);

            IMathExpression result = operand1.Multiply(operand2);

            var resultData = result.Data as PolynomialData;
            Assert.IsNotNull(resultData);
            Assert.IsNotEmpty(resultData.Values);
            Assert.AreEqual(3, resultData.Values.Count);

            var freeValue = resultData.Values[0].Data as ScalarData;
            Assert.IsNotNull(freeValue);
            Assert.AreEqual(15.0, freeValue.Value);

            var parameterValue = resultData.Values[1].Data as ScalarData;
            Assert.IsNotNull(parameterValue);
            Assert.AreEqual(41.0, parameterValue.Value);

            var parameterQuadValue = resultData.Values[2].Data as ScalarData;
            Assert.IsNotNull(parameterQuadValue);
            Assert.AreEqual(28.0, parameterQuadValue.Value);
        }

        [Test]
        public void PolynomMultiplyDifferent()
        {
            IMathExpression operand1 = ExpressionFactory.Create(3.0, 4.0);
            IMathExpression operand2 = ExpressionFactory.Create(5.0, 7.0, "y");

            IMathExpression result = operand1.Multiply(operand2);

            var resultData = result.Data as PolynomialData;
            Assert.IsNotNull(resultData);
            Assert.IsNotEmpty(resultData.Values);
            Assert.AreEqual(2, resultData.Values.Count);

            var freeValue = resultData.Values[0].Data as PolynomialData;
            Assert.IsNotNull(freeValue);

            var parameterValue = resultData.Values[1].Data as PolynomialData;
            Assert.IsNotNull(parameterValue);

            var freeValueFree = freeValue.Values[0].Data as ScalarData;
            Assert.IsNotNull(freeValueFree);
            Assert.AreEqual(15.0, freeValueFree.Value);

            var freeValueParam = freeValue.Values[1].Data as ScalarData;
            Assert.IsNotNull(freeValueParam);
            Assert.AreEqual(21.0, freeValueParam.Value);

            var parameterValueFree = parameterValue.Values[0].Data as ScalarData;
            Assert.IsNotNull(parameterValueFree);
            Assert.AreEqual(20.0, parameterValueFree.Value);

            var parameterValueParameter = parameterValue.Values[1].Data as ScalarData;
            Assert.IsNotNull(parameterValueParameter);
            Assert.AreEqual(28.0, parameterValueParameter.Value);
        }

        [Test]
        public void ScalarReverce()
        {
            IMathExpression operand = ExpressionFactory.Create(4.0);
            IMathExpression result = operand.Reverse();
            var resultData = result.Data as ScalarData;
            Assert.IsNotNull(resultData);
            Assert.AreEqual(0.25, resultData.Value);
        }

        [Test]
        public void ScalarReverceException()
        {
            IMathExpression operand = ExpressionFactory.Create(0.0);
            Assert.Throws<DivideByZeroException>(() => operand.Reverse());
        }

        [Test]
        public void ScalarDivide()
        {
            IMathExpression nominator = ExpressionFactory.Create(5.0);
            IMathExpression denominator = ExpressionFactory.Create(7.0);
            IMathExpression divided = nominator.Divide(denominator);
            var data = divided.Data as ScalarData;
            Assert.IsNotNull(data);
            Assert.AreEqual(5.0/7.0, data.Value, GlobalVariables.EPSILON);
        }

        [Test]
        public void PolynomScalarDivide()
        {
            IMathExpression nominator = ExpressionFactory.Create(3.0, 7.0);
            IMathExpression denominator = ExpressionFactory.Create(5.0);
            IMathExpression divided = nominator.Divide(denominator);
            var data = divided.Data as PolynomialData;
            Assert.NotNull(data);
            Assert.AreEqual(3.0/5.0, (data.Values[0].Data as ScalarData).Value, GlobalVariables.EPSILON);
            Assert.AreEqual(7.0/5.0, (data.Values[1].Data as ScalarData).Value, GlobalVariables.EPSILON);
        }
    }
}